﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StateMachine<T>
{
    public delegate void StateUpdateAction(bool stateChanged, T inState, out T outState);
    public delegate void StatePreUpdateAction(T inState);
    public delegate void StatePostUpdateAction(bool stateChanged, T inState, T outState);
    private readonly Dictionary<T, StateUpdateAction> dictState = new();
    private T lastState = default;
    public StateMachine()
    {

    }

    public void Registe(T state, StateUpdateAction updateAction)
    {
        RegisteState(state, updateAction);
    }
    public void Registe(object o)
    {
        foreach (T t in System.Enum.GetValues(typeof(T)))
        {
            string updateFuncName = string.Format("UpdateState{0:G}", t.ToString());
            o.GetType().GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).FindWith((System.Reflection.MethodInfo m) =>
            {
                bool ret = false;
                if (updateFuncName.Equals(m.Name))
                {
                    try
                    {
                        StateUpdateAction d = (StateUpdateAction)m.CreateDelegate(typeof(StateUpdateAction), o);
                        RegisteState(t, d);
                        ret = true;
                    }
                    catch (System.Exception) { }
                }
                return ret;
            });
        }
    }

    public void UnRegiste(T state, StateUpdateAction updateAction)
    {
        UnRegisteState(state, updateAction);
    }

    public void UnRegiste(object o)
    {
        foreach (T t in System.Enum.GetValues(typeof(T)))
        {
            string funcName = string.Format("UpdateState{0:G}", t.ToString());
            o.GetType().GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).FindWith((System.Reflection.MethodInfo m) =>
            {
                bool ret = false;
                if (funcName.Equals(m.Name))
                {
                    try
                    {
                        StateUpdateAction d = (StateUpdateAction)m.CreateDelegate(typeof(StateUpdateAction), o);
                        UnRegisteState(t, d);
                        ret = true;
                    }
                    catch (System.Exception) { }
                }
                return ret;
            });

        }
    }


    public void RegisteState(T state, StateUpdateAction action)
    {
        if (dictState.ContainsKey(state))
        {
            dictState[state] += action;
        }
        else
        {
            dictState.Add(state, action);
        }
    }
    public bool UnRegisteState(T state, StateUpdateAction action)
    {
        bool ret = false;
        if (dictState.ContainsKey(state))
        {
            dictState[state] -= action;
        }
        return ret;
    }

    public bool InvokState(bool stateChanged, T inState, out T outState)
    {
        bool ret = false;
        outState = inState;
        if (NoAction())
        {
            Debug.Log("No any action registed!");
        }
        if (TryGetValue(inState, out StateUpdateAction action))
        {
            action.Invoke(stateChanged, inState, out outState);
            //foreach (System.Delegate d in action.GetInvocationList())
            //{
            //    d.DynamicInvoke(new object[] { innerStateChanged, inState, outState });
            //    if (!inState.Equals(outState))
            //    {
            //        break;
            //    }
            //}
            ret = true;
        }
        return ret;
    }
    public bool NoAction()
    {
        bool noAction = dictState.Count == 0;
        if (noAction)
        {
            bool found = false;
            foreach (StateUpdateAction va in dictState.Values)
            {
                if (va != null)
                {
                    found = true;
                    break;
                }
            }
            noAction = !found;
        }
        return noAction;
    }

    private bool TryGetValue(T state, out StateUpdateAction action)
    {
        bool ret = false;
        if (dictState.ContainsKey(state))
        {
            action = dictState[state];
            if (action != null)
            {
                ret = true;
            }
        }
        else
        {
            action = null;
        }
        return ret;
    }



    public void Invoke(T inState, out T outState, StateUpdateAction defaultAction = null, StatePreUpdateAction preAction = null, StatePostUpdateAction postAction = null)
    {
        if (preAction != null)
        {
            preAction(inState);
        }
        bool innerStateChanged = !inState.Equals(lastState);
        lastState = inState;
        if (!this.InvokState(innerStateChanged, inState, out outState) && defaultAction != null)
        {
            //如果没有找到匹配的状态行为，则执行默认的行为
            defaultAction(innerStateChanged, inState, out outState);
        }
        //innerStateChanged = !inState.Equals(outState);


        if (postAction != null)
        {
            postAction(innerStateChanged, inState, outState);
        }
    }
}
